﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Cqrs.Infrastructure;
using Meditime.Domain;
using Cqrs.Infrastructure.Aggregate;
using Cqrs.Common;

namespace Meditime.UI
{
    public partial class AddShiftTime : Form
    {
        private class DropDownTransition
        {
            public string Command { get; set; }
            public string ResultingState { get; set; }
        }

        private Guid _memberId;
        private int _versioNo = 0;
        private Guid _shiftId = Guid.NewGuid();
        private Action _submitCommand;

        public AddShiftTime()
        {
            InitializeComponent();
        }

        public Action AddShift(Guid staffMemberId)
        {
            PopulateForm(staffMemberId);
            return AddShift();
        }

        public Action AddShift()
        {
            this.ShowDialog();
            return _submitCommand;
        }

        private void AddShiftTime_Load(object sender, EventArgs e)
        {
            dtShiftStartTime.Value = DateTime.Now;
        }

        private void RefreshStatesCombo(Guid memberId, string currentState)
        {
            btnAdd.Enabled = false;
            cboStates.DataSource = null;
            cboStates.Items.Clear();
            cboStates.Items.Add("Please wait, loading states...");
            cboStates.SelectedIndex = 0;

            cboStates.Enabled = false;
            BackgroundWorker worker = new BackgroundWorker();
            worker.DoWork += (s, e) =>
            {
                try
                {
                    e.Result = GetAvailableTransitions(memberId, currentState);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(Common.FormatError(ex));
                }
            };
            worker.RunWorkerCompleted += OnWorkerCompleted;
            worker.RunWorkerAsync();
        }

        delegate void OnWorkerCompletedMethod(object sender, RunWorkerCompletedEventArgs e);
        private void OnWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            try
            {
                if (cboStates.InvokeRequired)
                {
                    cboStates.Invoke(new OnWorkerCompletedMethod(OnWorkerCompleted), new object[] { sender, e });
                    return;
                }
                Dictionary<string, Transition> states = e.Result as Dictionary<string, Transition>;
                List<DropDownTransition> availableStates = new List<DropDownTransition>();
                states.ToList().ForEach(p => availableStates.Add
                    (
                        new DropDownTransition()
                        {
                            Command = p.Value.Command,
                            ResultingState = p.Value.ResultingState
                        }
                    )
                    );
                cboStates.DataSource = availableStates;
                cboStates.ValueMember = "Command";
                cboStates.DisplayMember = "ResultingState";
                cboStates.Enabled = true;
                btnAdd.Enabled = true;
            }
            catch (Exception ex)
            {
                CommandNotifier.Instance.WriteToTextBox(Common.FormatError(ex), true);
            }
        }

        private void btnFindMember_Click(object sender, EventArgs e)
        {
            try
            {
                StaffMembers members = new StaffMembers();
                var member = members.GetStaffMember();
                if (member != null)
                {
                    _memberId = member.Id;
                    PopulateForm(_memberId);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void PopulateForm(Guid memberId)
        {
            Guid shiftId = Guid.NewGuid();
            int versionNo = 0;

            var currentShift = GetCurrentShiftStatus(memberId);
            if (currentShift == null)
            {
                txtCurrentStatus.Text = Domain.ShiftRecording.States.Created;
            }
            else
            {
                versionNo = currentShift.VersionNo;
                txtCurrentStatus.Text = currentShift.CurrentShiftState;
                shiftId = currentShift.ShiftsId;
            }

            txtMemberName.Text = GetMemberName(memberId);
            RefreshStatesCombo(memberId, txtCurrentStatus.Text);
            _shiftId = shiftId;
            _memberId = memberId;
            _versioNo = versionNo;

        }

        private string GetMemberName(Guid id)
        {
            using (ReadModelEntities readModelDb = new ReadModelEntities())
            {

                return (from n in readModelDb.StaffMembers
                        where n.Id == id
                        select n.Name).FirstOrDefault();
            }
        }

        private CurrentShiftStatus GetCurrentShiftStatus(Guid id)
        {
            using (ReadModelEntities readModelDb = new ReadModelEntities())
            {
                return (from n in readModelDb.CurrentShiftStatuses
                        where n.StaffMemberId == id
                        select n).FirstOrDefault();
            }
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            try
            {
                this.Close();
            }
            catch { }
        }

        private void btnAdd_Click(object sender, EventArgs e)
        {
            try
            {
                if (cboStates.SelectedItem == null)
                    return;
                _submitCommand = CreateCommandAction(
                    ((DropDownTransition)cboStates.SelectedItem).Command);
                this.Close();

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private Action CreateCommandAction(string commandName)
        {
            switch (commandName)
            {
                case ShiftRecordingCommands.AddToRoster:
                    AddToRoster command = new AddToRoster(_shiftId, dtShiftStartTime.Value, _memberId, _versioNo);
                    return () => ShiftTimeCommands.Instance().Submit(command);

                case ShiftRecordingCommands.GoOnHoliday:
                    GoOnHoliday goOnHoliday = new GoOnHoliday(_shiftId, dtShiftStartTime.Value, _memberId, _versioNo);
                    return () => ShiftTimeCommands.Instance().Submit(goOnHoliday);

                case ShiftRecordingCommands.MarkAsOffDuty:
                    MarkAsOffDuty markAsOffDuty = new MarkAsOffDuty(_shiftId, dtShiftStartTime.Value, _memberId, _versioNo);
                    return () => ShiftTimeCommands.Instance().Submit(markAsOffDuty);

                case ShiftRecordingCommands.RemoveFromRoster:
                    RemoveFromRoster removeFromRoster = new RemoveFromRoster(_shiftId, dtShiftStartTime.Value, _memberId, _versioNo);
                    return () => ShiftTimeCommands.Instance().Submit(removeFromRoster);

                case ShiftRecordingCommands.StartOnCall:
                    StartOnCall startOnCall = new StartOnCall(_shiftId, dtShiftStartTime.Value, _memberId, _versioNo);
                    return () => ShiftTimeCommands.Instance().Submit(startOnCall);

                case ShiftRecordingCommands.StartWorking:
                    StartWorking startWorking = new StartWorking(_shiftId, dtShiftStartTime.Value, _memberId, _versioNo);
                    return () => ShiftTimeCommands.Instance().Submit(startWorking);

                default:
                    throw new ApplicationException(string.Format("Command {0} not found.", commandName));
            }
        }

        private Dictionary<string, Transition> GetAvailableTransitions(Guid id, string currentState)
        {
            ShiftRecording shiftRecording = new ShiftRecording(EventStore.ShiftRecordingSmf);
            var shift = ShiftTimeCommands.Repository.GetById(shiftRecording, id);
            var stateMachine = shift.GetStateMachine();
            var stateDictionary = stateMachine[currentState];
            return stateDictionary.Transitions;
        }
    }
}
